package com.webchat.pgc.service.mall;


import com.google.common.base.Joiner;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.webchat.common.constants.WebConstant;
import com.webchat.common.enums.RedisKeyEnum;
import com.webchat.common.enums.mall.MallSourceTypeEnum;
import com.webchat.common.enums.mall.MallStatusEnum;
import com.webchat.common.enums.messagequeue.MessageBroadChannelEnum;
import com.webchat.common.service.RedisService;
import com.webchat.common.service.localcache.AbstractLocalCacheService;
import com.webchat.common.service.messagequeue.producer.MessageQueueProducer;
import com.webchat.common.util.JsonUtil;
import com.webchat.common.util.ThreadPoolExecutorUtil;
import com.webchat.common.util.TransactionSyncManagerUtil;
import com.webchat.domain.dto.queue.RefreshLocalCacheMessage;
import com.webchat.domain.vo.request.mall.MallSPURequestVO;
import com.webchat.domain.vo.response.mall.MallCategoryBaseLevelVO;
import com.webchat.domain.vo.response.mall.MallPropertyResponseVO;
import com.webchat.domain.vo.response.mall.MallSKUDetailVO;
import com.webchat.domain.vo.response.mall.MallSKUResponseVO;
import com.webchat.domain.vo.response.mall.MallSPUBaseVO;
import com.webchat.domain.vo.response.mall.MallSPUDetailVO;
import com.webchat.domain.vo.response.mall.MallSPUListVO;
import com.webchat.domain.vo.response.mall.MallSPUPropertyIdListVO;
import com.webchat.domain.vo.response.mall.MallShopBaseVO;
import com.webchat.domain.vo.response.mall.MallShopResponseVO;
import com.webchat.pgc.constants.LocalCacheConstants;
import com.webchat.pgc.repository.dao.mall.IMallSKUDAO;
import com.webchat.pgc.repository.dao.mall.IMallSPUDAO;
import com.webchat.pgc.repository.entity.mall.MallSKU;
import com.webchat.pgc.repository.entity.mall.MallSKUProperty;
import com.webchat.pgc.repository.entity.mall.MallSPU;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MallSPUService extends AbstractPgcLocalCacheService<Long> {


    @Autowired
    private IMallSPUDAO mallSPUDAO;

    @Autowired
    private MallImageService mallImageService;
    @Autowired
    private MallSKUService mallSKUService;
    @Autowired
    private MallPropertyService mallPropertyService;
    @Autowired
    private MallShopService mallShopService;
    @Autowired
    private MallCategoryService mallCategoryService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private IMallSKUDAO iMallSKUDAO;

    /**
     * 创建，更新商品
     * @param spuRequest
     * @return
     */
    @Transactional
    public Long save(MallSPURequestVO spuRequest) {
        // 1. 持久化SPU
        MallSPU spu = convert(spuRequest);
        spu = mallSPUDAO.save(spu);
        Long spuId = spu.getId();
        // 2. 持久化SPU图片信息（1:n）
        mallImageService.save(MallSourceTypeEnum.SPU, spuId, spuRequest.getImages());
        // 3. 保存SKU及SKU对应销售属性数据
        mallSKUService.save(spuId, spuRequest.getSkus(), spuRequest.getOperator());
        // 4. 刷新缓存
        TransactionSyncManagerUtil.afterCommit(() -> refreshProductCache(spuId));
        return spuId;
    }


    /**
     * 发布商品
     *
     * @param spuId
     * @param userId
     * @return
     */
    public boolean publish(Long spuId, String userId) {

        this.updateSpuStatus(spuId, userId, MallStatusEnum.PUBLISHED);
        log.info("商品上架 ===> 操作人:{}, 商品:{}", userId, spuId);
        return true;
    }

    /**
     * 发布商品
     *
     * @param spuId
     * @param userId
     * @return
     */
    public boolean invoke(Long spuId, String userId) {

        this.updateSpuStatus(spuId, userId, MallStatusEnum.UNPUBLISHED);
        log.info("商品下架 ===> 操作人:{}, 商品:{}", userId, spuId);
        return true;
    }

    /**
     * 发布商品
     *
     * @param spuId
     * @param userId
     * @return
     */
    public boolean delete(Long spuId, String userId) {

        this.updateSpuStatus(spuId, userId, MallStatusEnum.DELETED);
        log.info("商品删除 ===> 操作人:{}, 商品:{}", userId, spuId);
        return true;
    }

    private boolean updateSpuStatus(Long spuId, String userId, MallStatusEnum status) {
        MallSPU spu = mallSPUDAO.findById(spuId).orElse(null);
        Assert.notNull(spu, "操作失败：商品不存在！");

        // TODO 梳理商品状态机流转，校验当前状态是否允许当前操作
        // TODO 需要鉴权

        spu.setStatus(status.getStatus());
        spu.setUpdateBy(userId);
        spu.setUpdateDate(new Date());
        mallSPUDAO.save(spu);
        // 刷新商品详情缓存
        ThreadPoolExecutorUtil.execute(() -> refreshProductCache(spuId));
        return true;
    }



    /**
     * 刷新商品详情缓存
     *
     * @return
     */
    public MallSKUDetailVO refreshProductCache(Long spuId) {

        // 1. 查询SPU数据
        MallSPU spu = mallSPUDAO.findById(spuId).orElse(null);
        if (spu == null) {
            return null;
        }
        // 2. 查询SKU数据
        List<MallSKU> skus = mallSKUService.getBySpuId(spuId);
        List<Long> skuIdList = skus.stream().map(MallSKU::getId).toList();
        // 3. 查询SKU销售属性数据
        List<MallSKUProperty> skuProperties = mallSKUService.getPropertyBySkuIdIn(skuIdList);
        // 4. 查询销售属性名数据
        List<Long> propertyNameIdList = skuProperties.stream().map(MallSKUProperty::getPropertyNameId).distinct().toList();
        Map<Long, MallPropertyResponseVO> propertyNameMap = mallPropertyService.getPropertyNameMap(propertyNameIdList);
        // 5. 查询销售属性值数据
        List<Long> propertyValueIdList = skuProperties.stream().map(MallSKUProperty::getPropertyValueId).distinct().toList();
        Map<Long, MallPropertyResponseVO> propertyValueMap = mallPropertyService.getPropertyValueMap(propertyValueIdList);
        // 6. 获取SPU图片配置
        List<String> images = mallImageService.getImagesBySourceTypeAndId(MallSourceTypeEnum.SPU, spuId);
        // 7. 构建缓存对象
        MallSKUDetailVO product = buildSKUDetailVO(spu, skus, skuProperties, propertyNameMap, propertyValueMap, images);
        // 8. 商品数据缓存
        return doCache(product);
    }


    /**
     * 查询商品详情
     *
     * @return
     */

    public MallSPUDetailVO detail(Long spuId, String userId) {

        MallSPUDetailVO product = this.detail(spuId);

        // TODO 需要用户商品浏览数据埋点，用于浏览历史、feed推荐等场景

        return product;
    }

    public MallSPUDetailVO detail(Long spuId) {

        MallSPUDetailVO productDetail = new MallSPUDetailVO();
        // 1. 先取商品数据
        MallSKUDetailVO product = this.getCache(spuId);
        BeanUtils.copyProperties(product, productDetail);
        // 2. 再取店铺数据
        MallShopResponseVO shop = new MallShopResponseVO();
        MallShopBaseVO shopBase = mallShopService.detail(product.getShopId());
        BeanUtils.copyProperties(shopBase, shop);
        // TODO 获取店铺销售数据/评分数据等
        productDetail.setShop(shop);
        return productDetail;
    }

    public MallSPUDetailVO detailBySku(Long skuId) {

        // 1. 根据SKUID查询对应SPUID
        Long spuId = getProductIdBySku(skuId);
        if (spuId == null) {
            return null;
        }
        // 2. 根据SPUID查询商品缓存
        return detail(spuId);
    }

    /**
     * 根据skuId批量查询spu、shop信息
     *
     * @param skuIdList
     * @return Map<skuId, MallSKUDetailVO>
     */
    public Map<Long, MallSPUDetailVO> batchGetProductBySkuIdList(List<Long> skuIdList) {

        if (CollectionUtils.isEmpty(skuIdList)) {
            return Collections.emptyMap();
        }
        // 1. 批量查询 skuId -> spuId
        Map<Long, Long> skuSpuIdMap = this.batchGetProductIdBySkuId(new HashSet<>(skuIdList));
        if (MapUtils.isEmpty(skuSpuIdMap)) {
            return Collections.emptyMap();
        }
        // 2. 批量根据spuId -> product
        // 2.1 批量取本地缓存product
        List<Long> productIdSet = skuSpuIdMap.values().stream().toList();
        // 2.2 本地缓存不存在的部分spu走redis批量取
        // 2.3 走mysql取
        Map<Long, MallSKUDetailVO> productMap = this.batchGetMap(productIdSet);
        if (MapUtils.isEmpty(productMap)) {
            return Collections.emptyMap();
        }
        // 3. 根据shopId批量查询redis
        // 从获取到的所有商品数据中获取对应的店铺id集
        List<Long> shopIdList = productMap.values().stream().map(MallSKUDetailVO::getShopId).distinct().toList();
        Map<Long, MallShopBaseVO> shopMap = mallShopService.batchGetMap(shopIdList);
        Map<Long, MallSPUDetailVO> productDetailMap = new HashMap<>();
        for (Long skuId : skuIdList) {
            Long spuId = skuSpuIdMap.get(skuId);
            if (spuId == null) {
                continue;
            }
            MallSKUDetailVO skuDetail = productMap.get(spuId);
            if (skuDetail == null) {
                continue;
            }
            MallShopBaseVO shop = shopMap.get(skuDetail.getShopId());
            if (shop == null) {
                continue;
            }
            MallSPUDetailVO productDetail = new MallSPUDetailVO();
            BeanUtils.copyProperties(skuDetail, productDetail);
            MallShopResponseVO shopDetail = new MallShopResponseVO();
            BeanUtils.copyProperties(shop, shopDetail);
            productDetail.setShop(shopDetail);
            productDetailMap.put(skuId, productDetail);
        }
        return productDetailMap;
    }

    /**
     * TODO 后续简化下上架管理后台详情
     *
     * @param spuId
     * @return
     */
    public MallSPUDetailVO detailForBackend(Long spuId) {

        // TODO

        return null;
    }

    /**
     * 店铺下最新上架
     *
     * @param shopId
     * @param pageNo
     * @param pageSize
     * @return
     */
    public List<MallSPUListVO> newList(Long shopId, String pageNo, String pageSize) {

        // TODO
        return null;
    }

    /**
     * 店铺下热门商品
     *
     * @param shopId
     * @param pageNo
     * @param pageSize
     * @return
     */
    public List<MallSPUListVO> hotList(Long shopId, String pageNo, String pageSize) {

        // TODO
        return null;
    }


    /**
     * 走redis批量查询商品详情
     *
     * @param productIdList
     * @return
     */
    public List<MallSKUDetailVO> batchGetList(List<Long> productIdList) {

        // 优先走本地缓存批量读
        Map<Long, MallSKUDetailVO> localProductMap = LocalCacheConstants.MALL_PRODUCT_LOCAL_CACHE.getAllPresent(productIdList);
        // 取本地缓存存在的商品id
        Set<Long> existProductIds = localProductMap.keySet();
        // 删除所有存在的id
        if (CollectionUtils.isNotEmpty(existProductIds)) {
            productIdList.removeAll(new ArrayList<>(existProductIds));
        }
        if (CollectionUtils.isEmpty(productIdList)) {
            // 全集删除本地缓存存在的商品id后结果为空，说明这批次查询所有商品都可以走本地缓存获取到，不需要在redis查询了
            return (List<MallSKUDetailVO>) localProductMap.values();
        }
        List<MallSKUDetailVO> productList = new ArrayList<>();
        // 获取到本地缓存中不存在商品id(productIdList)，需要走redis批量查询
        List<String> productRedisCacheKeys = productIdList.stream().map(pid ->
                RedisKeyEnum.MALL_PRODUCT_DETAIL_CACHE.getKey(String.valueOf(pid))).toList();
        List<String> caches = redisService.mget(productRedisCacheKeys);
        for (int i = 0; i < productIdList.size(); i++) {
            Long productId = productIdList.get(i);
            String cache = caches.get(i);
            MallSKUDetailVO productDetail;
            if (StringUtils.isNotBlank(cache)) {
                productDetail = JsonUtil.fromJson(cache, MallSKUDetailVO.class);
            } else {
                productDetail = refreshProductCache(productId);
            }
            if (productDetail != null) {
                // 找到商品，加入返回结果集
                productList.add(productDetail);
            }
        }
        if (MapUtils.isNotEmpty(localProductMap)) {
            // 追加上本地缓存拿到的商品数据
            productList.addAll(localProductMap.values());
        }
        return productList;
    }

    /**
     * 批量查询商品数据
     *
     * @param spuIds
     * @return
     */
    public Map<Long, MallSKUDetailVO> batchGetMap(List<Long> spuIds) {

        List<MallSKUDetailVO> productList = this.batchGetList(spuIds);
        if (CollectionUtils.isEmpty(productList)) {
            return Collections.emptyMap();
        }
        return productList.stream().collect(
                Collectors.toMap(MallSKUDetailVO::getId, p -> p, (p1, p2) -> p1));
    }


    public MallSPU convert(MallSPURequestVO spuRequest) {
        Long id = spuRequest.getId();
        String operator = spuRequest.getOperator();
        Date now = new Date();
        MallSPU spu;
        if (id != null) {
            spu = get(id);
            Assert.notNull(spu, "商品信息更新失败：商品不存在");
        } else {
            spu = new MallSPU();
            spu.setShopId(spuRequest.getShopId());
            spu.setStatus(MallStatusEnum.NEW.getStatus());
            spu.setCreateBy(operator);
            spu.setCreateDate(now);
        }
        spu.setCategoryId(spuRequest.getCategoryId());
        spu.setTitle(spuRequest.getTitle());
        spu.setMainTitle(spuRequest.getMainTitle());
        spu.setVideo(spuRequest.getVideo());
        spu.setDescription(spuRequest.getDescription());
        spu.setDetail(spu.getDetail());
        spu.setCommitment(spuRequest.getCommitment());
        spu.setUpdateBy(operator);
        spu.setUpdateDate(now);
        return spu;
    }

    private MallSPU get(Long spuId) {
        return mallSPUDAO.findById(spuId).orElse(null);
    }

    /**
     * 查询电商商品详情缓存
     *
     * @param spuId
     * @return
     */
    public MallSKUDetailVO getCache(Long spuId) {
        // 1. 走两级缓存获取商品详情
        MallSKUDetailVO product = getProductFromTowLevelCache(spuId);
        if (product != null) {
            return product;
        }
        String lockKey = RedisKeyEnum.MALL_PRODUCT_REFRESH_LOCK.getKey(String.valueOf(spuId));
        RLock rLock = redissonClient.getLock(lockKey);
        // 阻塞获取锁
        rLock.lock();
        try {
            product = getProductFromTowLevelCache(spuId);
            if (product != null) {
                return product;
            }
            // 3. 主动刷新缓存
            product = refreshProductCache(spuId);
        } catch (Exception e) {
            log.error("商品详情缓存刷新异常 ===> 商品ID:{}", spuId, e);
        } finally {
            rLock.unlock();
        }
        return product;
    }


    /**
     * 走两级缓存获取商品详情
     *
     * @return
     */
    private MallSKUDetailVO getProductFromTowLevelCache(Long spuId) {
        // 1. 先读本地缓存
        MallSKUDetailVO product = LocalCacheConstants.MALL_PRODUCT_LOCAL_CACHE.getIfPresent(spuId);
        if (product != null) {
            return product;
        }
        // 2. 在读redis
        String redisCacheKey = RedisKeyEnum.MALL_PRODUCT_DETAIL_CACHE.getKey(String.valueOf(spuId));
        String productCache = redisService.get(redisCacheKey);
        if (StringUtils.isNotBlank(productCache)) {
            product = JsonUtil.fromJson(productCache, MallSKUDetailVO.class);
            doLocalCache(product);
        }
        return product;
    }

    /**
     * 缓存商品详情
     *
     * @param product
     * @return
     */
    private MallSKUDetailVO doCache(MallSKUDetailVO product) {

        if (product == null) {
            return null;
        }
        // 刷新 redis 二级缓存
        doRedisCache(product);
        // 刷新本地一级缓存
        super.doSendRefreshLocalCacheMessage(product.getId(), "MALL_PRODUCT_LOCAL_CACHE");
        // 刷新sku->spu映射缓存缓存
        List<Long> skuIdList = product.getSkuList().stream().map(MallSKUResponseVO::getId).toList();
        doCacheSkuSpuMapping(product.getId(), skuIdList);
        return product;
    }

    /**
     * 根据SKUid反查spuId数据
     *
     * @param skuId
     * @return
     */
    private Long getProductIdBySku(Long skuId) {
        String cacheKey = skuSpuMappingCacheKey();
        String spuIdStr = redisService.hget(cacheKey, String.valueOf(skuId));
        if (StringUtils.isNotBlank(spuIdStr)) {
            if (WebConstant.CACHE_NONE.equals(spuIdStr)) {
                // 防止缓存击穿（缓存了一个空串）
                return null;
            }
            return Long.valueOf(spuIdStr);
        }
        // 缓存中不存在：1.缓存失效、2.缓存击穿（不存在的sku）
        String key = RedisKeyEnum.MALL_REFRESH_SKU_SPU_MAPPING_LOCK.getKey(String.valueOf(skuId));
        RLock rLock = redissonClient.getLock(key);
        rLock.lock();
        try {
            spuIdStr = redisService.hget(cacheKey, String.valueOf(skuId));
            if (StringUtils.isNotBlank(spuIdStr)) {
                if (WebConstant.CACHE_NONE.equals(spuIdStr)) {
                    // 防止缓存击穿（缓存了一个空串）
                    return null;
                }
                return Long.valueOf(spuIdStr);
            }
            return doCacheSkuSpuMapping(skuId);
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 批量查询sku对应的spuId
     *
     * @param skuIdList
     * @return
     */
    private Map<Long, Long> batchGetProductIdBySkuId(Set<Long> skuIdList) {

        if (CollectionUtils.isEmpty(skuIdList)) {
            return Collections.emptyMap();
        }
        Map<Long, Long> skuSpuMap = new HashMap<>(skuIdList.size());
        String cacheKey = skuSpuMappingCacheKey();
        List<String> hashKeys = skuIdList.stream().map(String::valueOf).toList();
        List<String> values = redisService.hmget(cacheKey, hashKeys);
        for (int i = 0; i < hashKeys.size(); i++) {
            Long skuId = Long.valueOf(hashKeys.get(i));
            String value = values.get(i);
            Long productId;
            if (StringUtils.isNotBlank(value)) {
                productId = Long.valueOf(value);
            } else {
                productId = doCacheSkuSpuMapping(skuId);
            }
            skuSpuMap.put(skuId, productId);
        }
        return skuSpuMap;
    }

    /**
     * 刷新sku对应spuid缓存
     *
     * ps：这里如果sku被删除，hash中skuId一致存在（走调度每天0:00检查清理一次）
     *
     * @param productId
     */
    private void doCacheSkuSpuMapping(Long productId, List<Long> skuIdList) {
        String productIdStr = productId == null ? WebConstant.CACHE_NONE : String.valueOf(productId);
        String cacheKey = skuSpuMappingCacheKey();
        Map<String, String> hash = new HashMap<>();
        for (Long skuId : skuIdList) {
            hash.put(skuId.toString(), productIdStr);
        }
        redisService.hsetAll(cacheKey, hash, RedisKeyEnum.MALL_SKU_SPU_MAPPING_HASH_CACHE.getExpireTime());
    }

    private Long doCacheSkuSpuMapping(Long skuId) {
        MallSKU sku = iMallSKUDAO.findById(skuId).orElse(null);
        Long productId = null;
        if (sku != null) {
            productId = sku.getSpuId();
        }
        doCacheSkuSpuMapping(productId, List.of(skuId));
        return productId;
    }

    private MallSKUDetailVO doLocalCache(MallSKUDetailVO product) {
        // 刷新本地一级缓存
        LocalCacheConstants.MALL_PRODUCT_LOCAL_CACHE.put(product.getId(), product);
        return product;
    }

    private MallSKUDetailVO doRedisCache(MallSKUDetailVO product) {
        // 刷新本地一级缓存
        String redisCacheKey = RedisKeyEnum.MALL_PRODUCT_DETAIL_CACHE.getKey(String.valueOf(product.getId()));
        redisService.set(redisCacheKey, JsonUtil.toJsonString(product), RedisKeyEnum.MALL_PRODUCT_DETAIL_CACHE.getExpireTime());
        return product;
    }

    private String skuSpuMappingCacheKey() {
        return RedisKeyEnum.MALL_SKU_SPU_MAPPING_HASH_CACHE.getKey();
    }



    /**
     * 构建商品详情缓存对象
     *
     * @param spu                  商品SPU
     * @param skus                 商品SPU配置的SKU列表
     * @param skuProperties        SKU对应的销售属性配置
     * @param propertyNameMap      当前商品SKU使用到销售属性名
     * @param propertyValueMap     当前商品SKU使用到销售属性值
     * @return
     */
    private MallSKUDetailVO buildSKUDetailVO(MallSPU spu,
                                             List<MallSKU> skus,
                                             List<MallSKUProperty> skuProperties,
                                             Map<Long, MallPropertyResponseVO> propertyNameMap,
                                             Map<Long, MallPropertyResponseVO> propertyValueMap,
                                             List<String> images) {

        MallSKUDetailVO product = new MallSKUDetailVO();

        // SPU基础数据
        BeanUtils.copyProperties(spu, product);
        product.setImages(images);
        if (spu.getPublishDate() != null) {
            // 商品最新一次上架时间戳
            product.setPublishTime(spu.getPublishDate().getTime());
        }
        Map<Long, List<MallSKUProperty>> skuPropertyMap = skuProperties.stream()
                                                            .collect(Collectors.groupingBy(
                                                                    MallSKUProperty::getSkuId,
                                                                    LinkedHashMap::new,
                                                                    Collectors.toList()
                                                            ));
        // SKU数据列表
        List<MallSKUResponseVO> skuVOList = skus.stream().map(sku -> {
            MallSKUResponseVO skuVO = new MallSKUResponseVO();
            BeanUtils.copyProperties(sku, skuVO);
            List<MallSKUProperty> skuPropertyList = skuPropertyMap.get(sku.getId());
            Map<Long, Long> skuMap = skuPropertyList.stream()
                    .collect(Collectors.toMap(
                            MallSKUProperty::getPropertyNameId,
                            MallSKUProperty::getPropertyValueId));
            List<String> skuPropertyValueList =
                    skuPropertyList.stream().map(skuProperty ->
                    propertyValueMap.get(skuProperty.getPropertyValueId()).getVal())
                    .toList();
            String propertyValueKey = Joiner.on(WebConstant.SEMICOLON_SPLIT_CHAR).join(skuPropertyValueList);
            skuVO.setSkuMap(skuMap);
            skuVO.setPropertyValueKey(propertyValueKey);
            return skuVO;
        }).toList();
        product.setSkuList(skuVOList);
        // 销售属性数据
        Map<Long, String> propertyNameMapper = propertyNameMap.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> ((MallPropertyResponseVO) entry.getValue()).getVal()
                ));
        Map<Long, String> propertyValueMapper = propertyValueMap.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> ((MallPropertyResponseVO) entry.getValue()).getVal()
                ));
        product.setPropertyNameMap(propertyNameMapper);
        product.setPropertyValueMap(propertyValueMapper);
        // 构建销售属性配置关系
        // <propertyNameId, List<propertyValueId>>
        Map<Long, List<Long>> mallSPUPropertyIdListVO = skuProperties.stream()
                .collect(Collectors.groupingBy(
                        MallSKUProperty::getPropertyNameId,
                        Collectors.mapping(
                                MallSKUProperty::getPropertyValueId,
                                Collectors.collectingAndThen(
                                        Collectors.toList(),
                                        list -> list.stream().distinct().toList()  // 更简洁
                                )
                        )
                ));
        List<MallSPUPropertyIdListVO> properties = mallSPUPropertyIdListVO.entrySet().stream()
                .map(entry -> {
                        MallSPUPropertyIdListVO propertyIdListVO = new MallSPUPropertyIdListVO();
                        propertyIdListVO.setPropertyNameId(entry.getKey());
                        propertyIdListVO.setPropertyValueIdList(entry.getValue());
                        return propertyIdListVO;
                    }).toList();
        product.setProperties(properties);
        return product;
    }
}
